package com.github.ghsea.sso.client.security.shiro;

import com.github.ghsea.sso.client.security.cas.RequestContext;
import com.github.ghsea.sso.client.security.cas.WebUtil;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.util.StringUtils;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 有些应用的ContextPath不为/时，会导致生成的session cookie的path为ContextPath。
 * * 而ContextPath为/的生成的session cookie的path为/。这会导致应用间的session cookie不能共享。
 * * 因此提供此类，将session cookie的Path固定为"/"。
 *
 * @author guhai
 */
public class FixedPathSimpleCookie extends SimpleCookie {

    private Logger log = LoggerFactory.getLogger(FixedPathSimpleCookie.class);

    private String sameSite;

    protected static final String SAME_SITE_ATTRIBUTE_NAME = "SameSite";

    private final List<String> SAME_SITE_VALUES = Arrays.asList("None", "Strict", "Lax");

    public FixedPathSimpleCookie() {
        super();
    }

    public FixedPathSimpleCookie(String name) {
        super(name);
    }

    public FixedPathSimpleCookie(Cookie cookie) {
        super(cookie);
    }

    /**
     * 覆盖SimpleCookie.saveTo()中的path的计算逻辑
     */
    @Override
    public void saveTo(HttpServletRequest request, HttpServletResponse response) {

        String name = getName();
        String value = getValue();
        String comment = getComment();
        String domain = getDomain();
        String path = SimpleCookie.ROOT_PATH;
        int maxAge = getMaxAge();
        int version = getVersion();
        boolean secure = isSecure();

        addCookieHeader(response, name, value, comment, domain, path, maxAge, version, secure, isHttpOnly());
        holdCookie(request, value);
        log.info("created cookie:{}", value);
    }

    private void addCookieHeader(HttpServletResponse response, String name, String value, String comment,
                                 String domain, String path, int maxAge, int version,
                                 boolean secure, boolean httpOnly) {
        String headerValue = buildHeaderValue(name,
                value,
                comment,
                domain,
                path,
                maxAge,
                version,
                secure,
                httpOnly,
                this.sameSite);
        response.addHeader(COOKIE_HEADER_NAME, headerValue);
    }

    protected String buildHeaderValue(String name, String value, String comment,
                                      String domain, String path, int maxAge, int version,
                                      boolean secure, boolean httpOnly, String sameSite) {
        String headerValue = super.buildHeaderValue(name, value, comment, domain, path, maxAge, version, secure, httpOnly);
        return appendSameSite(headerValue, sameSite);
    }

    private String appendSameSite(String oldHeaderValue, String sameSite) {
        StringBuffer sb = new StringBuffer(oldHeaderValue);
        if (StringUtils.hasText(sameSite) && SAME_SITE_VALUES.contains(sameSite)) {
            sb.append(ATTRIBUTE_DELIMITER);
            sb.append(SAME_SITE_ATTRIBUTE_NAME).append(NAME_VALUE_DELIMITER).append(sameSite);
        }
        return sb.toString();
    }

    private void holdCookie(final HttpServletRequest request, String cookie) {
        Optional<RequestContext> contextOptional = WebUtil.getRequestContext(request);
        RequestContext context = null;
        if (!contextOptional.isPresent()) {
            context = new RequestContext();
        }
        context.setCookie(cookie);
        WebUtil.holdRequestContext(request, context);
    }


    public FixedPathSimpleCookie setSameSite(String sameSite) {
        this.sameSite = sameSite;
        return this;
    }

}
